`include "defines.svh"
`include "axi_defines.svh"
`default_nettype none

module axi_xbar #(
    parameter SRAM_BASE_ADDR  = 32'h8000_0000, // SRAM基地址
    parameter SRAM_ADDR_MASK  = 32'hFF00_0000,
    parameter CLINT_BASE_ADDR = 32'hA000_0048, // CLINT基地址
    parameter CLINT_ADDR_MASK = 32'hFFFF_FFF8,
    parameter DEV_BASE_ADDR   = 32'hA000_0000, // DEV基地址(接到ram上)
    parameter DEV_ADDR_MASK   = 32'hFF00_0000
)(
    // Master Interface
    input  data_t m_debug_pc,
    input  logic  m_awvalid,
    output logic  m_awready,
    input  addr_t m_awaddr,

    input  logic  m_wvalid,
    output logic  m_wready,
    input  data_t m_wdata,
    input  strb_t m_wstrb,

    output logic  m_bvalid,
    input  logic  m_bready,
    output resp_t m_bresp,

    input  logic  m_arvalid,
    output logic  m_arready,
    input  addr_t m_araddr,

    output logic  m_rvalid,
    input  logic  m_rready,
    output data_t m_rdata,
    output resp_t m_rresp,

    // SRAM Slave Interface
    output data_t sram_debug_pc,
    output logic  sram_awvalid,
    input  logic  sram_awready,
    output addr_t sram_awaddr,

    output logic  sram_wvalid,
    input  logic  sram_wready,
    output data_t sram_wdata,
    output strb_t sram_wstrb,

    input  logic  sram_bvalid,
    output logic  sram_bready,
    input  resp_t sram_bresp,

    output logic  sram_arvalid,
    input  logic  sram_arready,
    output addr_t sram_araddr,

    input  logic  sram_rvalid,
    output logic  sram_rready,
    input  data_t sram_rdata,
    input  resp_t sram_rresp,

    // CLINT Slave Interface
    output logic  clint_awvalid,
    input  logic  clint_awready,
    output addr_t clint_awaddr,

    output logic  clint_wvalid,
    input  logic  clint_wready,
    output data_t clint_wdata,
    output strb_t clint_wstrb,

    input  logic  clint_bvalid,
    output logic  clint_bready,
    input  resp_t clint_bresp,

    output logic  clint_arvalid,
    input  logic  clint_arready,
    output addr_t clint_araddr,

    input  logic  clint_rvalid,
    output logic  clint_rready,
    input  data_t clint_rdata,
    input  resp_t clint_rresp
);

    // Address decoding
    wire sram_selected = ((m_awaddr & SRAM_ADDR_MASK) == SRAM_BASE_ADDR) ||
                          ((m_araddr & SRAM_ADDR_MASK) == SRAM_BASE_ADDR);
    wire clint_selected = ((m_araddr & CLINT_ADDR_MASK) == CLINT_BASE_ADDR);
    wire dev_selected = (((m_awaddr & DEV_ADDR_MASK) == DEV_BASE_ADDR) ||
                        ((m_araddr & DEV_ADDR_MASK) == DEV_BASE_ADDR)) && ~clint_selected;

    assign sram_debug_pc = m_debug_pc;

    // Write Address Channel
    assign sram_awvalid = m_awvalid && (sram_selected || dev_selected);
    assign clint_awvalid = m_awvalid && clint_selected;
    assign sram_awaddr  = m_awaddr;
    assign clint_awaddr  = m_awaddr;
    assign m_awready    = (sram_selected  ? sram_awready  : `OFF) |
                          (clint_selected ? clint_awready : `OFF) |
                          (dev_selected   ? sram_awready  : `OFF);

    // Write Data Channel
    assign sram_wvalid = m_wvalid && (sram_selected || dev_selected);
    assign clint_wvalid = m_wvalid && clint_selected;
    assign sram_wdata  = m_wdata;
    assign clint_wdata  = m_wdata;
    assign sram_wstrb  = m_wstrb;
    assign clint_wstrb  = m_wstrb;
    assign m_wready    = (sram_selected  ? sram_wready  : `OFF) |
                         (clint_selected ? clint_wready : `OFF) |
                         (dev_selected   ? sram_wready  : `OFF);

    // Write Response Channel
    assign m_bvalid = (sram_selected  ? sram_bvalid  : `OFF) |
                      (clint_selected ? clint_bvalid : `OFF) |
                      (dev_selected   ? sram_bvalid  : `OFF);
    assign m_bresp  = (sram_selected  ? sram_bresp   : `NULL) |
                      (clint_selected ? clint_bresp  : `NULL) |
                      (dev_selected   ? sram_bresp   : `NULL);
    assign sram_bready = m_bready && (sram_selected || dev_selected);
    assign clint_bready = m_bready && clint_selected;

    // Read Address Channel
    assign sram_arvalid = m_arvalid && (sram_selected || dev_selected);
    assign clint_arvalid = m_arvalid && clint_selected;
    assign sram_araddr  = m_araddr;
    assign clint_araddr  = m_araddr;
    assign m_arready    = (sram_selected  ? sram_arready  : `OFF) |
                          (clint_selected ? clint_arready : `OFF) |
                          (dev_selected   ? sram_arready  : `OFF);

    // Read Data Channel
    assign m_rvalid = (sram_selected  ? sram_rvalid  : `OFF) |
                      (clint_selected ? clint_rvalid : `OFF) |
                      (dev_selected   ? sram_rvalid  : `OFF);

    assign m_rdata  = (sram_selected  ? sram_rdata   : `NULL) |
                      (clint_selected ? clint_rdata  : `NULL) |
                      (dev_selected   ? sram_rdata   : `NULL);

    assign m_rresp  = (sram_selected  ? sram_rresp   : `NULL) |
                      (clint_selected ? clint_rresp  : `NULL) |
                      (dev_selected   ? sram_rresp   : `NULL);

    assign sram_rready = m_rready && (sram_selected || dev_selected);
    assign clint_rready = m_rready && clint_selected;

endmodule
